#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
GitLab TimescaleDB同步脚本

主要功能：
- 支持全量和增量同步GitLab数据到TimescaleDB
- 命令行参数配置
- 多项目并行处理
- 详细的进度报告和错误处理
"""

import argparse
import json
import sys
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime, timedelta
from pathlib import Path
from typing import Optional

from loguru import logger

# 添加包路径以支持直接运行
sys.path.insert(0, str(Path(__file__).parent.parent))

from gitlab_timescale.config import get_config, setup_logging, validate_config, print_config
from gitlab_timescale.models import TimescaleDBManager
from gitlab_timescale.collector import GitLabTimescaleCollector, SyncConfig
from gitlab_timescale.analyzer import GitLabTimescaleAnalyzer, AnalysisConfig


def parse_arguments():
    """解析命令行参数"""
    parser = argparse.ArgumentParser(
        description="GitLab TimescaleDB数据同步工具",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用示例:
  # 全量同步所有项目
  python sync_script.py --full-sync
  
  # 增量同步（从昨天开始）
  python sync_script.py --start-time "2024-01-01 00:00:00"
  
  # 同步指定项目
  python sync_script.py --projects 123,456,789 --full-sync
  
  # 初始化数据库架构
  python sync_script.py --init-db
  
  # 生成分析报告
  python sync_script.py --analyze --start-time "2024-01-01" --end-time "2024-01-31"
        """
    )

    # 同步模式
    sync_group = parser.add_mutually_exclusive_group()
    sync_group.add_argument(
        '--full-sync',
        action='store_true',
        help='执行全量同步（从头开始收集所有数据）'
    )
    sync_group.add_argument(
        '--start-time',
        type=str,
        help='增量同步开始时间 (格式: YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS)'
    )

    parser.add_argument(
        '--end-time',
        type=str,
        help='同步结束时间 (格式: YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS)'
    )

    # 项目过滤
    parser.add_argument(
        '--projects',
        type=str,
        help='指定要同步的项目ID列表，用逗号分隔 (例如: 123,456,789)'
    )

    # 同步配置
    parser.add_argument(
        '--batch-size',
        type=int,
        default=100,
        help='批处理大小 (默认: 100)'
    )

    parser.add_argument(
        '--max-workers',
        type=int,
        default=5,
        help='最大并发线程数 (默认: 5)'
    )

    parser.add_argument(
        '--exclude-merge-commits',
        action='store_true',
        help='排除合并提交'
    )

    # 数据库操作
    parser.add_argument(
        '--init-db',
        action='store_true',
        help='初始化数据库架构'
    )

    parser.add_argument(
        '--schema-file',
        type=str,
        help='数据库架构文件路径 (默认: gitlab_timescale_schema.sql)'
    )

    # 分析功能
    parser.add_argument(
        '--analyze',
        action='store_true',
        help='生成分析报告'
    )

    parser.add_argument(
        '--output-format',
        choices=['json', 'csv'],
        default='json',
        help='输出格式 (默认: json)'
    )

    parser.add_argument(
        '--output-file',
        type=str,
        help='输出文件路径'
    )

    # 配置选项
    parser.add_argument(
        '--gitlab-url',
        type=str,
        help='GitLab服务器URL'
    )

    parser.add_argument(
        '--gitlab-token',
        type=str,
        help='GitLab访问令牌'
    )

    parser.add_argument(
        '--db-url',
        type=str,
        help='数据库连接字符串'
    )

    # 日志选项
    parser.add_argument(
        '--log-level',
        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR'],
        default='INFO',
        help='日志级别 (默认: INFO)'
    )

    parser.add_argument(
        '--log-file',
        type=str,
        help='日志文件路径'
    )

    parser.add_argument(
        '--quiet',
        action='store_true',
        help='静默模式（不输出到控制台）'
    )

    # 多实例选项
    parser.add_argument(
        '--multi-instance',
        action='store_true',
        help='启用多GitLab实例同步模式'
    )

    parser.add_argument(
        '--instance-name',
        type=str,
        help='指定要同步的GitLab实例名称（单实例模式）'
    )

    parser.add_argument(
        '--multi-workers',
        type=int,
        default=2,
        help='多实例同步的最大并发数 (默认: 2)'
    )

    # 其他选项
    parser.add_argument(
        '--dry-run',
        action='store_true',
        help='试运行模式（不实际执行同步）'
    )

    parser.add_argument(
        '--config-only',
        action='store_true',
        help='仅显示配置信息'
    )

    return parser.parse_args()


def parse_datetime(date_str: str) -> Optional[datetime]:
    """解析日期时间字符串"""
    if not date_str:
        return None

    formats = [
        '%Y-%m-%d %H:%M:%S',
        '%Y-%m-%d %H:%M',
        '%Y-%m-%d',
        '%Y/%m/%d %H:%M:%S',
        '%Y/%m/%d %H:%M',
        '%Y/%m/%d'
    ]

    for fmt in formats:
        try:
            return datetime.strptime(date_str, fmt)
        except ValueError:
            continue

    raise ValueError(f"无法解析日期时间格式: {date_str}")


def initialize_database(db_manager: TimescaleDBManager, schema_file: Optional[str] = None) -> bool:
    """初始化数据库"""
    logger.info("开始初始化数据库架构...")

    if not schema_file:
        # 查找默认架构文件
        current_dir = Path(__file__).parent.parent
        schema_file = current_dir / "gitlab_timescale_schema.sql"

    schema_path = Path(schema_file)
    if not schema_path.exists():
        logger.error(f"架构文件不存在: {schema_path}")
        return False

    try:
        success = db_manager.execute_schema_file(str(schema_path))
        if success:
            logger.info("数据库架构初始化成功")
        else:
            logger.error("数据库架构初始化失败")
        return success
    except Exception as e:
        logger.error(f"初始化数据库架构时发生异常: {e}")
        return False


def perform_sync(collector: GitLabTimescaleCollector, sync_config: SyncConfig, dry_run: bool = False) -> dict:
    """执行同步操作"""
    if dry_run:
        logger.info("=== 试运行模式 ===")
        logger.info(f"同步配置: {sync_config}")
        return {'status': 'dry_run', 'message': '试运行完成'}

    logger.info("开始执行GitLab数据同步...")

    try:
        # 测试连接
        if not collector.test_connection():
            return {'status': 'error', 'message': '连接测试失败'}

        # 执行同步
        results = collector.sync_all_projects(sync_config)

        logger.info("=== 同步结果汇总 ===")
        logger.info(f"总项目数: {results['total_projects']}")
        logger.info(f"总分支数: {results['total_branches']}")
        logger.info(f"总提交数: {results['total_commits']}")
        logger.info(f"开始时间: {results['start_time']}")
        logger.info(f"结束时间: {results['end_time']}")

        if results['failed_projects']:
            logger.warning(f"失败项目数: {len(results['failed_projects'])}")
            for failed in results['failed_projects']:
                logger.warning(f"  项目 {failed['project_id']}: {failed['error']}")

        return {'status': 'success', 'results': results}

    except Exception as e:
        logger.error(f"同步过程中发生异常: {e}")
        return {'status': 'error', 'message': str(e)}


def perform_analysis(analyzer: GitLabTimescaleAnalyzer, analysis_config: AnalysisConfig,
                     output_format: str = 'json', output_file: Optional[str] = None) -> dict:
    """执行分析操作"""
    logger.info("开始生成分析报告...")

    try:
        # 生成汇总报告
        report = analyzer.generate_summary_report(analysis_config)

        if 'error' in report:
            return {'status': 'error', 'message': report['error']}

        # 输出结果
        if output_file:
            output_path = Path(output_file)

            if output_format == 'json':
                with open(output_path, 'w', encoding='utf-8') as f:
                    json.dump(report, f, ensure_ascii=False, indent=2, default=str)
                logger.info(f"分析报告已保存到: {output_path}")

            elif output_format == 'csv':
                # 导出主要统计数据为CSV
                import pandas as pd

                # 用户统计
                if report['user_stats']:
                    user_df = pd.DataFrame(report['user_stats'])
                    user_csv_path = output_path.with_suffix('.users.csv')
                    user_df.to_csv(user_csv_path, index=False, encoding='utf-8')
                    logger.info(f"用户统计已保存到: {user_csv_path}")

                # 项目统计
                if report['project_stats']:
                    project_df = pd.DataFrame(report['project_stats'])
                    project_csv_path = output_path.with_suffix('.projects.csv')
                    project_df.to_csv(project_csv_path, index=False, encoding='utf-8')
                    logger.info(f"项目统计已保存到: {project_csv_path}")

                # 时间序列
                if report['time_series']:
                    time_df = pd.DataFrame(report['time_series'])
                    time_csv_path = output_path.with_suffix('.timeseries.csv')
                    time_df.to_csv(time_csv_path, index=False, encoding='utf-8')
                    logger.info(f"时间序列已保存到: {time_csv_path}")

        else:
            # 输出到控制台
            if output_format == 'json':
                logger.info(json.dumps(report, ensure_ascii=False, indent=2, default=str))
            else:
                # 简化的文本输出
                logger.info("=== 分析报告汇总 ===")
                if 'summary' in report:
                    summary = report['summary']
                    logger.info(f"总贡献者数: {summary.get('total_contributors', 0)}")
                    logger.info(f"总项目数: {summary.get('total_projects', 0)}")
                    logger.info(f"总提交数: {summary.get('total_commits', 0)}")
                    logger.info(f"总代码行数变更: {summary.get('total_changes', 0)}")

        return {'status': 'success', 'report': report}

    except Exception as e:
        logger.error(f"分析过程中发生异常: {e}")
        return {'status': 'error', 'message': str(e)}


def sync_single_gitlab_instance(instance_name: str, instance_config, sync_config: SyncConfig,
                                db_manager: TimescaleDBManager, dry_run: bool = False) -> dict:
    """同步单个GitLab实例"""
    logger.info(f"开始同步GitLab实例: {instance_name} ({instance_config.url})")

    try:
        # 创建收集器
        collector = GitLabTimescaleCollector(
            gitlab_url=instance_config.url,
            access_token=instance_config.access_token,
            db_manager=db_manager
        )

        # 执行同步
        result = perform_sync(collector, sync_config, dry_run)
        result['instance_name'] = instance_name
        result['gitlab_url'] = instance_config.url

        return result

    except Exception as e:
        logger.error(f"GitLab实例 {instance_name} 同步失败: {e}")
        return {
            'status': 'error',
            'message': str(e),
            'instance_name': instance_name,
            'gitlab_url': instance_config.url
        }


def perform_multi_gitlab_sync(config, sync_config: SyncConfig, dry_run: bool = False,
                              max_workers: int = 2) -> dict:  # 降低默认并发数
    """执行多GitLab实例同步"""
    logger.info(f"开始多GitLab实例同步，共 {len(config.gitlab.instances)} 个实例")
    logger.info(f"使用 {max_workers} 个并发线程，每个实例最大超时时间: 60分钟")

    # 初始化数据库管理器
    db_manager = TimescaleDBManager(
        connection_string=config.database.connection_string,
        min_connections=config.database.min_connections,
        max_connections=config.database.max_connections
    )

    results = []
    start_time = datetime.now()

    # 使用线程池并发同步多个GitLab实例
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        # 提交所有同步任务
        future_to_instance = {
            executor.submit(
                sync_single_gitlab_instance,
                instance_name,
                instance_config,
                sync_config,
                db_manager,
                dry_run
            ): instance_name
            for instance_name, instance_config in config.gitlab.instances.items()
        }

        logger.info(f"已提交 {len(future_to_instance)} 个同步任务")

        # 收集结果，添加超时机制
        completed_count = 0
        total_count = len(future_to_instance)

        try:
            for future in as_completed(future_to_instance, timeout=7200):  # 2小时总超时
                instance_name = future_to_instance[future]
                completed_count += 1

                try:
                    # 每个实例60分钟超时
                    result = future.result(timeout=3600)
                    results.append(result)

                    elapsed = datetime.now() - start_time
                    if result['status'] == 'success':
                        logger.info(
                            f"[{completed_count}/{total_count}] GitLab实例 {instance_name} 同步成功 (耗时: {elapsed})")
                    else:
                        logger.error(
                            f"[{completed_count}/{total_count}] GitLab实例 {instance_name} 同步失败: {result['message']}")

                except TimeoutError:
                    logger.error(f"[{completed_count}/{total_count}] GitLab实例 {instance_name} 同步超时，跳过")
                    results.append({
                        'status': 'timeout',
                        'message': '同步超时',
                        'instance_name': instance_name
                    })
                    future.cancel()  # 尝试取消超时的任务

                except Exception as e:
                    logger.error(f"[{completed_count}/{total_count}] GitLab实例 {instance_name} 同步异常: {e}")
                    results.append({
                        'status': 'error',
                        'message': str(e),
                        'instance_name': instance_name
                    })

        except TimeoutError:
            logger.error("多实例同步总体超时，强制结束")
            # 取消所有未完成的任务
            for future in future_to_instance:
                if not future.done():
                    future.cancel()
                    instance_name = future_to_instance[future]
                    results.append({
                        'status': 'timeout',
                        'message': '总体超时，任务被取消',
                        'instance_name': instance_name
                    })

    # 汇总结果
    success_count = sum(1 for r in results if r['status'] == 'success')
    total_count = len(results)

    overall_status = 'success' if success_count == total_count else 'partial_success' if success_count > 0 else 'error'

    return {
        'status': overall_status,
        'message': f'多实例同步完成: {success_count}/{total_count} 成功',
        'results': results,
        'summary': {
            'total_instances': total_count,
            'successful_instances': success_count,
            'failed_instances': total_count - success_count
        }
    }


def main():
    """主函数"""
    args = parse_arguments()

    try:
        # 获取配置
        config = get_config()

        # 覆盖命令行参数
        if args.gitlab_url or args.gitlab_token:
            # 更新默认实例的配置
            default_instance = config.gitlab.get_default_instance()
            if args.gitlab_url:
                default_instance.url = args.gitlab_url
            if args.gitlab_token:
                default_instance.access_token = args.gitlab_token
        if args.db_url:
            config.database.connection_string = args.db_url
        if args.log_level:
            config.log.level = args.log_level
        if args.log_file:
            config.log.file_path = args.log_file
        if args.quiet:
            config.log.console_output = False

        # 设置日志
        setup_logging(config.log)

        # 验证配置
        if not validate_config(config):
            logger.error("配置验证失败")
            sys.exit(1)

        # 仅显示配置
        if args.config_only:
            print_config(config)
            return

        # 初始化数据库管理器
        db_manager = TimescaleDBManager(
            connection_string=config.database.connection_string,
            min_connections=config.database.min_connections,
            max_connections=config.database.max_connections
        )

        # 初始化数据库架构
        if args.init_db:
            success = initialize_database(db_manager, args.schema_file)
            if not success:
                sys.exit(1)
            return

        # 分析模式
        if args.analyze:
            analyzer = GitLabTimescaleAnalyzer(db_manager)

            # 确定要分析的GitLab实例
            gitlab_url = None
            if args.instance_name and args.instance_name in config.gitlab.instances:
                gitlab_url = config.gitlab.instances[args.instance_name].url
            elif not args.multi_instance:
                gitlab_url = config.gitlab.get_default_instance().url

            analysis_config = AnalysisConfig(
                gitlab_url=gitlab_url,
                start_date=parse_datetime(args.start_time).date() if args.start_time else None,
                end_date=parse_datetime(args.end_time).date() if args.end_time else None,
                project_ids=[int(x.strip()) for x in args.projects.split(',')] if args.projects else None,
                include_merge_commits=not args.exclude_merge_commits
            )

            result = perform_analysis(analyzer, analysis_config, args.output_format, args.output_file)

            if result['status'] != 'success':
                logger.error(f"分析失败: {result['message']}")
                sys.exit(1)

            return

        # 同步模式
        if not args.full_sync and not args.start_time:
            # 默认增量同步（从昨天开始）
            args.start_time = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')
            logger.info(f"未指定同步模式，默认从昨天开始增量同步: {args.start_time}")

        # 创建同步配置
        sync_config = SyncConfig(
            full_sync=args.full_sync,
            start_time=parse_datetime(args.start_time) if args.start_time else None,
            end_time=parse_datetime(args.end_time) if args.end_time else None,
            batch_size=args.batch_size,
            max_workers=args.max_workers,
            include_merge_commits=not args.exclude_merge_commits,
            projects_filter=[int(x.strip()) for x in args.projects.split(',')] if args.projects else None
        )

        # 多实例同步模式
        if args.multi_instance:
            result = perform_multi_gitlab_sync(config, sync_config, args.dry_run, args.multi_workers)

            if result['status'] == 'error':
                logger.error(f"多实例同步失败: {result['message']}")
                sys.exit(1)
            elif result['status'] == 'partial_success':
                logger.warning(f"多实例同步部分成功: {result['message']}")
            else:
                logger.info(f"多实例同步完成: {result['message']}")

            # 刷新物化视图
            if not args.dry_run:
                logger.info("开始刷新物化视图...")
                db_manager.refresh_materialized_views()

        # 单实例同步模式
        else:
            # 确定要使用的GitLab实例
            if args.instance_name:
                if args.instance_name not in config.gitlab.instances:
                    logger.error(f"未找到GitLab实例: {args.instance_name}")
                    sys.exit(1)
                instance_config = config.gitlab.instances[args.instance_name]
            else:
                instance_config = config.gitlab.get_default_instance()

            # 创建收集器
            collector = GitLabTimescaleCollector(
                gitlab_url=instance_config.url,
                access_token=instance_config.access_token,
                db_manager=db_manager
            )

            # 执行同步
            result = perform_sync(collector, sync_config, args.dry_run)

            if result['status'] != 'success':
                logger.error(f"同步失败: {result['message']}")
                sys.exit(1)

            # 刷新物化视图
            if not args.dry_run:
                logger.info("开始刷新物化视图...")
                db_manager.refresh_materialized_views()

        logger.info("同步操作完成")

    except KeyboardInterrupt:
        logger.info("用户中断操作")
        sys.exit(0)
    except Exception as e:
        logger.error(f"程序执行异常: {e}")
        sys.exit(1)


if __name__ == '__main__':
    main()
